/**********************************************************************************
 * 文件名  ：spi_flash.c
 * 描述    ：spi 底层应用函数库

 * 硬件连接 ----------------------------
 *         | PA4-SPI1-NSS  : W25X16-CS  |
 *         | PA5-SPI1-SCK  : W25X16-CLK |
 *         | PA6-SPI1-MISO : W25X16-DO  |
 *         | PA7-SPI1-MOSI : W25X16-DIO |
 *          ----------------------------
 *  兼容GD25Q32(通过基本读写测试)

**********************************************************************************/
#include "w25q64.h"

/* Private typedef -----------------------------------------------------------*/
// #define SPI_FLASH_PageSize      4096
#define SPI_FLASH_PageSize         256
#define SPI_FLASH_PerWritePageSize 256

/* Private define ------------------------------------------------------------*/
#define W25X_WriteEnable      0x06
#define W25X_WriteDisable     0x04
#define W25X_ReadStatusReg    0x05
#define W25X_WriteStatusReg   0x01
#define W25X_ReadData         0x03
#define W25X_FastReadData     0x0B
#define W25X_FastReadDual     0x3B
#define W25X_PageProgram      0x02
#define W25X_BlockErase       0xD8
#define W25X_SectorErase      0x20
#define W25X_ChipErase        0xC7
#define W25X_PowerDown        0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID         0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID    0x9F

#define WIP_Flag              0x01 /* Write In Progress (WIP) flag */

#define Dummy_Byte            0xFF

/*******************************************************************************
 * Function Name  : SPI_FLASH_Init
 * Description    : Initializes the peripherals used by the SPI FLASH driver.
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_Init(void) {
    rcu_periph_clock_enable(W25Q64_SPI_RCU);
    rcu_periph_clock_enable(W25Q64_SCL_GPIO_RCU);
    rcu_periph_clock_enable(W25Q64_MISO_GPIO_RCU);
    rcu_periph_clock_enable(W25Q64_MOSI_GPIO_RCU);
    rcu_periph_clock_enable(W25Q64_CS_GPIO_RCU);

    /* cs gpio */
    gpio_mode_set(W25Q64_CS_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, W25Q64_CS_GPIO_PIN); // 上拉（高电平不选择）
    gpio_output_options_set(W25Q64_CS_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, W25Q64_CS_GPIO_PIN);

    /* 硬件SPI GPIO复用 */
    gpio_mode_set(W25Q64_SCL_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, W25Q64_SCL_GPIO_PIN);
    gpio_mode_set(W25Q64_MISO_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, W25Q64_MISO_GPIO_PIN);
    gpio_mode_set(W25Q64_MOSI_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, W25Q64_MOSI_GPIO_PIN);

    gpio_af_set(W25Q64_SCL_GPIO_PORT, GPIO_AF_5, W25Q64_SCL_GPIO_PIN);
    gpio_af_set(W25Q64_MISO_GPIO_PORT, GPIO_AF_5, W25Q64_MISO_GPIO_PIN);
    gpio_af_set(W25Q64_MOSI_GPIO_PORT, GPIO_AF_5, W25Q64_MOSI_GPIO_PIN);

    /* 取消片选（拉高）Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();

    /* SPI1 configuration */
    // W25X16: data input on the DIO pin is sampled on the rising edge of the CLK.
    // Data on the DO and DIO pins are clocked out on the falling edge of CLK.
    spi_parameter_struct spi_init_struct;
    spi_i2s_deinit(W25Q64_SPI);

    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_4;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_crc_polynomial_set(W25Q64_SPI, 7);
    spi_init(W25Q64_SPI, &spi_init_struct);

    spi_enable(W25Q64_SPI);
    //  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    //  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    //  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    //  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    //  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    //  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    //  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
    //  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    //  SPI_InitStructure.SPI_CRCPolynomial = 7;
    //  SPI_Init(SPI1, &SPI_InitStructure);

    /* Enable SPI1  */
    // SPI_Cmd(SPI1, ENABLE);
}
/*******************************************************************************
 * Function Name  : SPI_FLASH_SectorErase
 * Description    : Erases the specified FLASH sector.
 * Input          : SectorAddr: address of the sector to erase.
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_SectorErase(u32 SectorAddr) {
    /* Send write enable instruction */
    SPI_FLASH_WriteEnable();
    SPI_FLASH_WaitForWriteEnd();
    /* Sector Erase */
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();
    /* Send Sector Erase instruction */
    SPI_FLASH_SendByte(W25X_SectorErase);
    /* Send SectorAddr high nibble address byte */
    SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
    /* Send SectorAddr medium nibble address byte */
    SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
    /* Send SectorAddr low nibble address byte */
    SPI_FLASH_SendByte(SectorAddr & 0xFF);
    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();
    /* Wait the end of Flash writing */
    SPI_FLASH_WaitForWriteEnd();
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_BulkErase
 * Description    : Erases the entire FLASH.
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_BulkErase(void) {
    /* Send write enable instruction */
    SPI_FLASH_WriteEnable();

    /* Bulk Erase */
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();
    /* Send Bulk Erase instruction  */
    SPI_FLASH_SendByte(W25X_ChipErase);
    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();

    /* Wait the end of Flash writing */
    SPI_FLASH_WaitForWriteEnd();
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_PageWrite
 * Description    : Writes more than one byte to the FLASH with a single WRITE
 *                  cycle(Page WRITE sequence). The number of byte can't exceed
 *                  the FLASH page size.
 * Input          : - pBuffer : pointer to the buffer  containing the data to be
 *                    written to the FLASH.
 *                  - WriteAddr : FLASH's internal address to write to.
 *                  - NumByteToWrite : number of bytes to write to the FLASH,
 *                    must be equal or less than "SPI_FLASH_PageSize" value.
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) {
    /* Enable the write access to the FLASH */
    SPI_FLASH_WriteEnable();

    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();
    /* Send "Write to Memory " instruction */
    SPI_FLASH_SendByte(W25X_PageProgram);
    /* Send WriteAddr high nibble address byte to write to */
    SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    /* Send WriteAddr medium nibble address byte to write to */
    SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    /* Send WriteAddr low nibble address byte to write to */
    SPI_FLASH_SendByte(WriteAddr & 0xFF);

    if (NumByteToWrite > SPI_FLASH_PerWritePageSize) {
        NumByteToWrite = SPI_FLASH_PerWritePageSize;
        // printf("\n\r Err: SPI_FLASH_PageWrite too large!");
    }

    /* while there is data to be written on the FLASH */
    while (NumByteToWrite--) {
        /* Send the current byte */
        SPI_FLASH_SendByte(*pBuffer);
        /* Point on the next byte to be written */
        pBuffer++;
    }

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();

    /* Wait the end of Flash writing */
    SPI_FLASH_WaitForWriteEnd();
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_BufferWrite
 * Description    : Writes block of data to the FLASH. In this function, the
 *                  number of WRITE cycles are reduced, using Page WRITE sequence.
 * Input          : - pBuffer : pointer to the buffer  containing the data to be
 *                    written to the FLASH.
 *                  - WriteAddr : FLASH's internal address to write to.
 *                  - NumByteToWrite : number of bytes to write to the FLASH.
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) {
    u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

    Addr        = WriteAddr % SPI_FLASH_PageSize;
    count       = SPI_FLASH_PageSize - Addr;
    NumOfPage   = NumByteToWrite / SPI_FLASH_PageSize;
    NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

    if (Addr == 0) /* WriteAddr is SPI_FLASH_PageSize aligned  */
    {
        if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
        {
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
        }
        else /* NumByteToWrite > SPI_FLASH_PageSize */
        {
            while (NumOfPage--) {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                WriteAddr += SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }

            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        }
    }
    else /* WriteAddr is not SPI_FLASH_PageSize aligned  */
    {
        if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
        {
            if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
            {
                temp = NumOfSingle - count;

                SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
                WriteAddr += count;
                pBuffer += count;

                SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
            }
            else {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
            }
        }
        else /* NumByteToWrite > SPI_FLASH_PageSize */
        {
            NumByteToWrite -= count;
            NumOfPage   = NumByteToWrite / SPI_FLASH_PageSize;
            NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

            SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
            WriteAddr += count;
            pBuffer += count;

            while (NumOfPage--) {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                WriteAddr += SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }

            if (NumOfSingle != 0) {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
            }
        }
    }
}

void SPI_FLASH_BufferWrite_Large(u8* pBuffer, u32 WriteAddr, u32 NumByteToWrite) {
    u32 startAddr = WriteAddr;
    u32 i;

    for (i = 0; i < NumByteToWrite; i++) {
        startAddr += i;
        SPI_FLASH_BufferWrite(pBuffer + i, startAddr, 1);
    }
}

void SPI_FLASH_BufferRead_Large(u8* pBuffer, u32 ReadAddr, u32 NumByteToRead) {
    u32 startAddr = ReadAddr;
    u32 i;

    for (i = 0; i < NumByteToRead; i++) {
        startAddr += i;
        SPI_FLASH_BufferRead(pBuffer + i, startAddr, 1);
    }
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_BufferRead
 * Description    : Reads a block of data from the FLASH.
 * Input          : - pBuffer : pointer to the buffer that receives the data read
 *                    from the FLASH.
 *                  - ReadAddr : FLASH's internal address to read from.
 *                  - NumByteToRead : number of bytes to read from the FLASH.
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead) {
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "Read from Memory " instruction */
    SPI_FLASH_SendByte(W25X_ReadData);

    /* Send ReadAddr high nibble address byte to read from */
    SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* Send ReadAddr medium nibble address byte to read from */
    SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* Send ReadAddr low nibble address byte to read from */
    SPI_FLASH_SendByte(ReadAddr & 0xFF);

    while (NumByteToRead--) /* while there is data to be read */
    {
        /* Read a byte from the FLASH */
        *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
        /* Point to the next location where the byte read will be saved */
        pBuffer++;
    }

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_ReadID
 * Description    : Reads FLASH identification.
 * Input          : None
 * Output         : None
 * Return         : FLASH identification
 *******************************************************************************/
u32 SPI_FLASH_ReadID(void) {
    u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "RDID " instruction */
    SPI_FLASH_SendByte(W25X_JedecDeviceID);

    /* Read a byte from the FLASH */
    Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

    /* Read a byte from the FLASH */
    Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

    /* Read a byte from the FLASH */
    Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();

    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

    return Temp;
}
/*******************************************************************************
 * Function Name  : SPI_FLASH_ReadID
 * Description    : Reads FLASH identification.
 * Input          : None
 * Output         : None
 * Return         : FLASH identification
 *******************************************************************************/
u32 SPI_FLASH_ReadDeviceID(void) {
    u32 Temp = 0;

    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "RDID " instruction */
    SPI_FLASH_SendByte(W25X_DeviceID);
    SPI_FLASH_SendByte(Dummy_Byte);
    SPI_FLASH_SendByte(Dummy_Byte);
    SPI_FLASH_SendByte(Dummy_Byte);

    /* Read a byte from the FLASH */
    Temp = SPI_FLASH_SendByte(Dummy_Byte);

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();

    return Temp;
}
/*******************************************************************************
 * Function Name  : SPI_FLASH_StartReadSequence
 * Description    : Initiates a read data byte (READ) sequence from the Flash.
 *                  This is done by driving the /CS line low to select the device,
 *                  then the READ instruction is transmitted followed by 3 bytes
 *                  address. This function exit and keep the /CS line low, so the
 *                  Flash still being selected. With this technique the whole
 *                  content of the Flash is read with a single READ instruction.
 * Input          : - ReadAddr : FLASH's internal address to read from.
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_StartReadSequence(u32 ReadAddr) {
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "Read from Memory " instruction */
    SPI_FLASH_SendByte(W25X_ReadData);

    /* Send the 24-bit address of the address to read from -----------------------*/
    /* Send ReadAddr high nibble address byte */
    SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* Send ReadAddr medium nibble address byte */
    SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* Send ReadAddr low nibble address byte */
    SPI_FLASH_SendByte(ReadAddr & 0xFF);
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_ReadByte
 * Description    : Reads a byte from the SPI Flash.
 *                  This function must be used only if the Start_Read_Sequence
 *                  function has been previously called.
 * Input          : None
 * Output         : None
 * Return         : Byte Read from the SPI Flash.
 *******************************************************************************/
u8 SPI_FLASH_ReadByte(void) {
    return (SPI_FLASH_SendByte(Dummy_Byte));
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_SendByte
 * Description    : Sends a byte through the SPI interface and return the byte
 *                  received from the SPI bus.
 * Input          : byte : byte to send.
 * Output         : None
 * Return         : The value of the received byte.
 *******************************************************************************/
u8 SPI_FLASH_SendByte(u8 byte) {
    /* Loop while DR register in not emplty */
    while (spi_i2s_flag_get(W25Q64_SPI, SPI_FLAG_TBE) == RESET);

    /* Send byte through the SPI1 peripheral */
    spi_i2s_data_transmit(W25Q64_SPI, byte);

    /* Wait to receive a byte */
    while (spi_i2s_flag_get(W25Q64_SPI, SPI_FLAG_RBNE) == RESET);

    /* Return the byte read from the SPI bus */
    return spi_i2s_data_receive(W25Q64_SPI);
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_SendHalfWord
 * Description    : Sends a Half Word through the SPI interface and return the
 *                  Half Word received from the SPI bus.
 * Input          : Half Word : Half Word to send.
 * Output         : None
 * Return         : The value of the received Half Word.
 *******************************************************************************/
u16 SPI_FLASH_SendHalfWord(u16 HalfWord) {
    /* Loop while DR register in not emplty */
    while (spi_i2s_flag_get(W25Q64_SPI, SPI_FLAG_TBE) == RESET);

    /* Send byte through the SPI1 peripheral */
    spi_i2s_data_transmit(W25Q64_SPI, HalfWord);

    /* Wait to receive a byte */
    while (spi_i2s_flag_get(W25Q64_SPI, SPI_FLAG_RBNE) == RESET);

    /* Return the byte read from the SPI bus */
    return spi_i2s_data_receive(W25Q64_SPI);
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_WriteEnable
 * Description    : Enables the write access to the FLASH.
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_WriteEnable(void) {
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "Write Enable" instruction */
    SPI_FLASH_SendByte(W25X_WriteEnable);

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();
}

/*******************************************************************************
 * Function Name  : SPI_FLASH_WaitForWriteEnd
 * Description    : Polls the status of the Write In Progress (WIP) flag in the
 *                  FLASH's status  register  and  loop  until write  opertaion
 *                  has completed.
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SPI_FLASH_WaitForWriteEnd(void) {
    u8 FLASH_Status = 0;

    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "Read Status Register" instruction */
    SPI_FLASH_SendByte(W25X_ReadStatusReg);

    /* Loop as long as the memory is busy with a write cycle */
    do {
        /* Send a dummy byte to generate the clock needed by the FLASH
        and put the value of the status register in FLASH_Status variable */
        FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);
    } while ((FLASH_Status & WIP_Flag) == SET); /* Write in progress */

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();
}

// 进入掉电模式
void SPI_Flash_PowerDown(void) {
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "Power Down" instruction */
    SPI_FLASH_SendByte(W25X_PowerDown);

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();
}

// 唤醒
void SPI_Flash_WAKEUP(void) {
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "Power Down" instruction */
    SPI_FLASH_SendByte(W25X_ReleasePowerDown);

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH(); // 等待TRES1
}

/******************************END OF FILE*****************************/
